package com.blog.aspect;

import com.blog.annotation.RequiredLog;
import com.blog.entity.Log;
import com.blog.entity.User;
import com.blog.mapper.LogMapper;
import com.blog.service.LogService;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.lang.reflect.Method;
import java.util.Date;

@Order(1)
@Aspect
@Component
public class LogAspect {

    @Pointcut("@annotation(com.blog.annotation.RequiredLog)")
    public void doLog(){}

    @Around("doLog()")
    public Object doLogAround(ProceedingJoinPoint joinPoint)throws  Throwable{
        Integer status=1;//ok
        String error="";
        Long time=0L;
        long t1=System.currentTimeMillis();
        try {
            Object result = joinPoint.proceed();
            long t2=System.currentTimeMillis();
            time=t2-t1;
            return result;
        }catch (Throwable e){
            long t3=System.currentTimeMillis();
            time=t3-t1;
            status=0;
            error=e.getMessage();
            throw e;
        }finally{
            saveUserLog(joinPoint,time,status,error);
        }
    }
    private void saveUserLog(ProceedingJoinPoint joinPoint,
                             Long time, Integer status, String error)
            throws NoSuchMethodException, JsonProcessingException {
        //1.获取用户行为日志
        //1.1获取用户ip,登陆用户
        //1.1.1 获取请求对象(在非Filter,Servlet，Controller对象中获取请求可以基于RequestContextHolder对象获取即可)
        ServletRequestAttributes requestAttributes=
                (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = requestAttributes.getRequest();
        //1.1.2获取ip地址
        String ip=request.getRemoteAddr();
        //1.1.3假如登录用户存储到了session，我可以从session获取用户信息
        String username="";
        User user= (User) request.getSession().getAttribute("user");
        if(user!=null){
            username=user.getUsername();
        }
        //1.2获取用户操作(切入点方法上RequiredLog注解内部operation的值)
        //1.2.1 获取目标类型(也就是你要执行的是哪个目标类中的方法)
        Class<?> targetCls=joinPoint.getTarget().getClass();
        //1.2.2 基于目标类型获取目标方法
        MethodSignature ms= (MethodSignature) joinPoint.getSignature();
        Method targetMethod=targetCls.getDeclaredMethod(ms.getName(),ms.getParameterTypes());
        //1.2.3获取注解对象中operation的值
        RequiredLog requiredLog = targetMethod.getAnnotation(RequiredLog.class);
        String operation=requiredLog.operation();
        //1.3用户执行操作时执行的什么方法(类全名+方法名)
        String method=targetCls.getName()+"."+targetMethod.getName();
        //1.4执行方法时传递的参数是什么？(建议以json格式进行记录)
        String params=//基于jackson中的api将参数转换为json格式字符串
                new ObjectMapper()
                        .writeValueAsString(joinPoint.getArgs());
        //2.封装用户行为日志
        Log userLog=new Log();
        userLog.setIp(ip);
        userLog.setUsername(username);
        userLog.setCreatedTime(new Date());
        userLog.setOperation(operation);
        userLog.setMethod(method);
        userLog.setParams(params);
        userLog.setTime(time);
        userLog.setStatus(status);
        userLog.setError(error);
        //3.记录用户行为日志(文件、数据库表、MQ)
        String threadName = Thread.currentThread().getName();
        System.out.println("saveUserLog.threadName="+threadName);

        //try{Thread.sleep(5000);}catch (Exception e){};
        //logMapper.insert(userLog);

       /* new Thread(new Runnable() {//并发量比较小可以
            @Override
            public void run() {
                try{Thread.sleep(5000);}catch (Exception e){};
                logMapper.insert(userLog);
            }
        }).start();*/
        logService.insert(userLog);
    }

    //@Autowired
    //private LogMapper logMapper;

    @Autowired
    private LogService logService;

}
